
package com.hlkj.pay.app.merchant.ext.impl.lakala;
/*
 * Hlpay-Plus aggregate payment system.
 * Copyright (c) 2024-2025 Hlpay Team Copyright has the right of final interpretation.
 */

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Repository;
import org.springframework.util.StringUtils;

import com.hlkj.framework.common.pojo.CommonResult;
import com.hlkj.framework.common.util.json.JsonUtils;
import com.hlkj.framework.extension.annotation.Extension;
import com.hlkj.framework.extension.executor.ExtensionExecutor;
import com.hlkj.framework.extension.scenario.BizScenario;
import com.hlkj.pay.app.merchant.ext.ChannelMerchantOrderExtPt;
import com.hlkj.pay.app.merchant.ext.MerchantExtConstant;
import com.hlkj.pay.app.merchant.ext.dto.order.ChannelPayOrderContext;
import com.hlkj.pay.app.merchant.ext.dto.order.ChannelPayOrderQueryContext;
import com.hlkj.pay.app.merchant.ext.dto.order.ChannelRefundPayOrderContext;
import com.hlkj.pay.app.merchant.ext.impl.huifu.handler.params.HuiFuPayOrderResponseParamsHandler;
import com.hlkj.pay.app.merchant.ext.impl.lakala.constant.LakalaExtConstant;
import com.hlkj.pay.app.merchant.ext.impl.lakala.dto.LakalaConfig;
import com.hlkj.pay.app.merchant.ext.impl.lakala.dto.resp.LakalaNotifyRes;
import com.hlkj.pay.app.merchant.ext.impl.lakala.dto.resp.LakalaPayOrderH5Res;
import com.hlkj.pay.app.merchant.ext.impl.lakala.dto.resp.LakalaRefundOrderRes;
import com.hlkj.pay.common.MerchantResultCode;
import com.hlkj.pay.dto.order.PayOrderChannelResultDto;
import com.hlkj.pay.dto.order.notify.CsOrderNotifyDto;
import com.hlkj.pay.dto.order.notify.RefundOrderNotifyDto;
import com.hlkj.pay.enums.PayEnums;
import com.hlkj.pay.enums.PayOrderEnums;
import com.hlkj.pay.infrastructure.model.order.PayOrderDO;
import com.hlkj.pay.infrastructure.model.order.RefundOrderDO;
import com.hlkj.pay.infrastructure.model.pay.PayWayDO;
import com.hlkj.pay.infrastructure.model.pay.ProviderChannelConfigDO;
import com.hlkj.pay.util.CommonSignUtil;
import com.hlkj.pay.util.DateUtils;
import com.hlkj.pay.util.HttpRequestUtil;
import com.hlkj.pay.util.MerchantUtils;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * 汇付订单操作
 * 
 * @author HlpayTeam
 * @date 2024/10/10 13:51
 */
@Slf4j
@Repository
@RequiredArgsConstructor
@Extension(bizId = MerchantExtConstant.CHANNEL_LAKALA)
public class LakalaMerchantOrderExtPt implements ChannelMerchantOrderExtPt {

    private final ExtensionExecutor extensionExecutor;

    @Override
    public CommonResult<String> channelOrderCode(ChannelPayOrderContext channelPayOrderContext) {
        return CommonResult.success();
    }

    @Override
    public void submitChannelPayOrder(ChannelPayOrderContext channelPayOrderContext) {
        PayWayDO payWayDO = channelPayOrderContext.getPayWayDO();
        String lakalaPay = lakalaPay(payWayDO);
        if (!StringUtils.hasText(lakalaPay)) {
            channelPayOrderContext.setRemark("当前支付方式不支持");
            channelPayOrderContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(LakalaScenePayOrderExtPt.class, BizScenario.valueOf(lakalaPay), ex -> ex.submitChannelPayOrder(channelPayOrderContext));
    }

    @Override
    public void queryChannelPayOrder(ChannelPayOrderQueryContext channelPayOrderQueryContext) {
        PayWayDO payWayDO = channelPayOrderQueryContext.getPayWayDO();
        String lakalaPay = lakalaPay(payWayDO);
        if (!StringUtils.hasText(lakalaPay)) {
            channelPayOrderQueryContext.setRemark("当前支付方式不支持");
            channelPayOrderQueryContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(LakalaScenePayOrderExtPt.class, BizScenario.valueOf(lakalaPay), ex -> ex.queryChannelPayOrder(channelPayOrderQueryContext));
    }

    @Override
    public void closeChannelPayOrder(ChannelPayOrderQueryContext channelPayOrderQueryContext) {
        PayWayDO payWayDO = channelPayOrderQueryContext.getPayWayDO();
        String lakalaPay = lakalaPay(payWayDO);
        if (!StringUtils.hasText(lakalaPay)) {
            channelPayOrderQueryContext.setRemark("当前支付方式不支持");
            channelPayOrderQueryContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(LakalaScenePayOrderExtPt.class, BizScenario.valueOf(lakalaPay), ex -> ex.closeChannelPayOrder(channelPayOrderQueryContext));
    }

    @Override
    public void queryChannelClosePayOrder(ChannelPayOrderQueryContext channelPayOrderQueryContext) {
        PayWayDO payWayDO = channelPayOrderQueryContext.getPayWayDO();
        String lakalaPay = lakalaPay(payWayDO);
        if (!StringUtils.hasText(lakalaPay)) {
            channelPayOrderQueryContext.setRemark("当前支付方式不支持");
            channelPayOrderQueryContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(LakalaScenePayOrderExtPt.class, BizScenario.valueOf(lakalaPay), ex -> ex.queryChannelClosePayOrder(channelPayOrderQueryContext));

    }

    @Override
    public boolean refundApply(ChannelRefundPayOrderContext channelRefundPayOrderContext) {
        return true;
    }

    @Override
    public void refundChannelPayOrder(ChannelRefundPayOrderContext channelRefundPayOrderContext) {
        PayWayDO payWayDO = channelRefundPayOrderContext.getPayWayDO();
        String lakalaPay = lakalaPay(payWayDO);
        if (!StringUtils.hasText(lakalaPay)) {
            channelRefundPayOrderContext.setRemark("当前支付方式不支持");
            channelRefundPayOrderContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(LakalaScenePayOrderExtPt.class, BizScenario.valueOf(lakalaPay), ex -> ex.refundChannelPayOrder(channelRefundPayOrderContext));
    }

    @Override
    public void queryRefundChannelPayOrder(ChannelRefundPayOrderContext channelRefundPayOrderContext) {
        PayWayDO payWayDO = channelRefundPayOrderContext.getPayWayDO();
        String lakalaPay = lakalaPay(payWayDO);
        if (!StringUtils.hasText(lakalaPay)) {
            channelRefundPayOrderContext.setRemark("当前支付方式不支持");
            channelRefundPayOrderContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(LakalaScenePayOrderExtPt.class, BizScenario.valueOf(lakalaPay), ex -> ex.queryRefundChannelPayOrder(channelRefundPayOrderContext));
    }

    @Override
    public CommonResult<PayOrderChannelResultDto> orderStatusParse(HttpServletRequest request, ProviderChannelConfigDO providerChannelConfigDO) {
        String authorization = request.getHeader("Authorization");
        String reqBody = HttpRequestUtil.requestBodyParams(request);
        // try {
        // // 获取完整的 JSON 字符串
        // reqBody = params(request);
        // }
        // catch (Exception e) {
        // log.error("从当前线程中获取参数失败 从流中重新获取失败:{}", e.getMessage());
        // return CommonResult.error(CommonResultCode.PARAMETER_MISSING);
        // }
        String paramsConfig = providerChannelConfigDO.getParamsConfig();
        CommonResult<PayOrderChannelResultDto> payOrderDOCommonResult = CommonResult.success();
        try {
            String[] split = authorization.split(",");
            String timestamp = split[0].split("=")[1].replace("\"", "");
            String nonce = split[1].split("=")[1].replace("\"", "");
            String signature = split[2].split("=")[1].replace("\"", "");
            String preSignData = timestamp + "\n" + nonce + "\n" + reqBody + "\n";
            // 配置转换
            LakalaConfig lakalaConfig = JsonUtils.parseObject(paramsConfig, LakalaConfig.class);
            boolean verify = CommonSignUtil.verify(lakalaConfig.getLklNotifyCerPath(), preSignData, signature);
            if (!verify) {
                log.info("拉卡拉回调验签不通过,signature:{}", signature);
                payOrderDOCommonResult.setCode(MerchantResultCode.MERCHANT_CHANNEL_CALLBACK_SIGN_ERROR.getCode());
            }
        }
        catch (Exception e) {
            e.printStackTrace();
            payOrderDOCommonResult.setCode(MerchantResultCode.MERCHANT_CHANNEL_CALLBACK_SIGN_ERROR.getCode());
        }
        Map<String, Object> paramsMap = JsonUtils.parseObject(reqBody, Map.class);
        if (paramsMap.containsKey("order_trade_info")) {
            // h5解析
            payOrderDOCommonResult.setData(h5Parse(reqBody));
        }
        else {
            // 普通解析
            payOrderDOCommonResult.setData(commonParse(reqBody));
        }
        log.info("拉卡拉收到回调,解析后的订单数据为 payOrderDO:{}", JsonUtils.toJsonStringNotNull(payOrderDOCommonResult.getData()));
        return payOrderDOCommonResult;
    }

    PayOrderChannelResultDto commonParse(String params) {
        PayOrderDO updartePayOrderDO = new PayOrderDO();
        LakalaNotifyRes respData = JsonUtils.parseObject(params, LakalaNotifyRes.class);
        switch (respData.getTrade_status()) {
            case "INIT":
            case "CREATE":
            case "DEAL":
            case "UNKNOWN":
                updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.PAYING.getCode());
                updartePayOrderDO.setBankMsg("");
                break;
            case "PART_REFUND":
            case "REFUND":
            case "SUCCESS":
                updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.SUCCESS.getCode());
                updartePayOrderDO.setBankMsg("");
                break;
            case "FAIL":
                updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.FAIL.getCode());
                break;
            case "CLOSE":
                updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.CLOSE.getCode());
                break;

        }
        if (StringUtils.hasText(respData.getTrade_time())) {
            try {
                updartePayOrderDO.setSuccessTime(DateUtils.shanghaiDate(respData.getTrade_time()));
            }
            catch (Exception e) {
                log.error("拉卡拉订单回调返回 时间格式化错误 dateTime:{},message:{}", respData.getTrade_time(), e.getMessage());
            }
        }
        updartePayOrderDO.setPayOrderNo(respData.getOut_trade_no());
        updartePayOrderDO.setInsOrderNo(respData.getAcc_trade_no());
        updartePayOrderDO.setChannelOrderId(respData.getLog_no());
        if (StringUtils.hasText(respData.getAcc_settle_amount())) {
            updartePayOrderDO.setSettlementAmount(MerchantUtils.fenToYuan(respData.getAcc_settle_amount()));
        }
        if (StringUtils.hasText(respData.getPayer_amount())) {
            updartePayOrderDO.setPayAmount(MerchantUtils.fenToYuan(respData.getPayer_amount()));
        }
        if (StringUtils.hasText(respData.getUser_id1())) {
            updartePayOrderDO.setUserId(respData.getUser_id1());
        }
        else {
            updartePayOrderDO.setUserId(respData.getUser_id2());
        }
        PayOrderChannelResultDto payOrderChannelResultDto = new PayOrderChannelResultDto();
        payOrderChannelResultDto.setUpdatePayOrderDO(updartePayOrderDO);
        payOrderChannelResultDto.setCallBackBody(params);
        return payOrderChannelResultDto;
    }

    PayOrderChannelResultDto h5Parse(String params) {
        PayOrderDO updartePayOrderDO = new PayOrderDO();
        LakalaPayOrderH5Res respData = JsonUtils.parseObject(params, LakalaPayOrderH5Res.class);
        // 0:待支付 1:支付中 2:支付成功 3:支付失败 4:已过期 5:已取消 6:部分退款或者全部退款 7:订单已关闭枚举
        switch (respData.getOrder_status()) {
            case "0":
            case "1":
                updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.PAYING.getCode());
                updartePayOrderDO.setBankMsg("");
                break;
            case "2":
            case "6":
                updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.SUCCESS.getCode());
                updartePayOrderDO.setBankMsg("");
                break;
            case "3":
                updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.FAIL.getCode());
                break;
            case "5":
                updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.CANCEL.getCode());
                break;
            case "4":
            case "7":
                updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.CLOSE.getCode());
                break;

        }
        LakalaPayOrderH5Res.OrderTradeInfoListBean orderTradeInfoListBean = respData.getOrder_trade_info();

        if (StringUtils.hasText(orderTradeInfoListBean.getTrade_time())) {
            try {
                updartePayOrderDO.setSuccessTime(DateUtils.shanghaiDate(orderTradeInfoListBean.getTrade_time()));
            }
            catch (Exception e) {
                log.error("拉卡拉H5订单回调返回 时间格式化错误 dateTime:{},message:{}", orderTradeInfoListBean.getTrade_time(), e.getMessage());
            }
        }
        updartePayOrderDO.setPayOrderNo(respData.getOut_order_no());
        updartePayOrderDO.setChannelOrderNo(orderTradeInfoListBean.getTrade_no());
        updartePayOrderDO.setInsOrderNo(orderTradeInfoListBean.getAcc_trade_no());
        updartePayOrderDO.setChannelOrderId(orderTradeInfoListBean.getLog_No());
        if (orderTradeInfoListBean.getPayer_amount() != null) {
            updartePayOrderDO.setPayAmount(MerchantUtils.fenToYuan(orderTradeInfoListBean.getPayer_amount()));
        }
        if (StringUtils.hasText(orderTradeInfoListBean.getUser_id1())) {
            updartePayOrderDO.setUserId(orderTradeInfoListBean.getUser_id1());
        }
        else {
            updartePayOrderDO.setUserId(orderTradeInfoListBean.getUser_id2());
        }
        PayOrderChannelResultDto payOrderChannelResultDto = new PayOrderChannelResultDto();
        payOrderChannelResultDto.setUpdatePayOrderDO(updartePayOrderDO);
        payOrderChannelResultDto.setCallBackBody(params);
        return payOrderChannelResultDto;
    }

    @Override
    public CommonResult<PayOrderChannelResultDto> orderStatusParse(HttpServletRequest request) {
        return null;
    }

    @Override
    public CommonResult<PayOrderChannelResultDto> merAuthParse(HttpServletRequest request) {
        return null;
    }

    @Override
    public PayOrderDO closeOrderStatusParse(HttpServletRequest request, ProviderChannelConfigDO providerChannelConfigDO) {
        return HuiFuPayOrderResponseParamsHandler.closeOrderStatusParse(request);
    }

    @Override
    public CommonResult<CsOrderNotifyDto> csOrderStatusParse(HttpServletRequest request, ProviderChannelConfigDO providerChannelConfigDO) {
        return null;
    }

    @Override
    public CommonResult<RefundOrderNotifyDto> refundStatusParse(HttpServletRequest request, ProviderChannelConfigDO providerChannelConfigDO) {
        String signature = request.getHeader("Authorization");
        String params = HttpRequestUtil.requestBodyParams(request);
        String paramsConfig = providerChannelConfigDO.getParamsConfig();
        CommonResult<RefundOrderNotifyDto> payOrderDOCommonResult = CommonResult.success();
        try {
            // 配置转换
            LakalaConfig lakalaConfig = JsonUtils.parseObject(paramsConfig, LakalaConfig.class);
            boolean verify = CommonSignUtil.verify(lakalaConfig.getLklNotifyCerPath(), params, signature);
            if (verify) {
                payOrderDOCommonResult.setCode(MerchantResultCode.MERCHANT_CHANNEL_CALLBACK_SIGN_ERROR.getCode());
            }
        }
        catch (Exception e) {
            payOrderDOCommonResult.setCode(MerchantResultCode.MERCHANT_CHANNEL_CALLBACK_SIGN_ERROR.getCode());
        }
        LakalaRefundOrderRes respData = JsonUtils.parseObject(params, LakalaRefundOrderRes.class);
        RefundOrderNotifyDto refundOrderNotifyDto=new RefundOrderNotifyDto();
        refundOrderNotifyDto.setCallBackBody(params);
        RefundOrderDO updateRefundOrderDO = new RefundOrderDO();
        refundOrderNotifyDto.setRefundOrder(updateRefundOrderDO);
        switch (respData.getTrade_status()) {
            case "SUCCESS":
                updateRefundOrderDO.setRefundStatus(PayOrderEnums.REFUND_STATUS.REFUND.getCode());
                break;
            case "FAIL":
            case "EXCEPTION":
                updateRefundOrderDO.setRefundStatus(PayOrderEnums.REFUND_STATUS.FAIL.getCode());
                break;
            default:
                updateRefundOrderDO.setRefundStatus(PayOrderEnums.REFUND_STATUS.REFUNDING.getCode());

        }
        updateRefundOrderDO.setRefundOrderNo(respData.getOut_trade_no());
        if (StringUtils.hasText(respData.getPayer_amount())) {
            updateRefundOrderDO.setActualRefundAmount(MerchantUtils.fenToYuan(respData.getPayer_amount()));
        }
        updateRefundOrderDO.setBankMsg(respData.getChannel_ret_desc());
        if (StringUtils.hasText(respData.getTrade_time())) {
            try {
                updateRefundOrderDO.setSuccessTime(DateUtils.shanghaiDate(respData.getTrade_time()));
            }
            catch (Exception e) {
                log.error("拉卡拉订单查询返回 时间格式化错误 dateTime:{},message:{}", respData.getTrade_time(), e.getMessage());
            }
        }

        payOrderDOCommonResult.setData(refundOrderNotifyDto);
        return payOrderDOCommonResult;
    }

    @Override
    public String channelPayCode(PayWayDO payWayDO) {
        return lakalaPay(payWayDO);
    }

    @Override
    public Long queryChannelPayAppid(ChannelPayOrderContext channelPayOrderContext) {
        return null;
    }
    @Override
    public String callBackResponse(CommonResult commonResult) {
        if (commonResult.isSuccess()) {
            return "{\"code\":\"SUCCESS\",\"message\":\"执行成功\"}";
        }
        else {
            return "{\"code\":\"FAIL\",\"message\":\"+" + commonResult.getMsg() + "\"}";
        }
    }

    @Override
    public boolean wechatLink(ChannelPayOrderContext channelPayOrderContext) {
        return false;
    }

    /**
     * 汇付支付方式路由
     * 
     * @param payWayDO
     * @return
     */
    private String lakalaPay(PayWayDO payWayDO) {
        PayEnums.SCENE_PAY_TYPE scenePayType = PayEnums.SCENE_PAY_TYPE.fromByCode(payWayDO.getCode());
        PayEnums.PAY_TYPE payType = PayEnums.PAY_TYPE.fromByCode(payWayDO.getType());
        if (scenePayType == null) {
            return null;
        }
        switch (scenePayType) {
            case SCAN:
                return LakalaExtConstant.SCAN_PAY;
            case H5:
            case WAP:
            case APP:
            case QR:
                switch (payType) {
                    case WECHAT:
                        return LakalaExtConstant.H5;
                    default:
                        return LakalaExtConstant.NATIVE_PAY;
                }
            default:
                return LakalaExtConstant.NATIVE_PAY;
        }
    }
}
